/**
 * @name Fizz Overflow
 * @description Narrowing conversions on untrusted data could enable
 *              an attacker to trigger an integer overflow.
 * @kind path-problem
 * @problem.severity warning
 */

import cpp
import semmle.code.cpp.ir.dataflow.TaintTracking
import semmle.code.cpp.ir.IR
import DataFlow::PathGraph

/**
 * The endianness conversion function `Endian::big()`.
 * It is Folly's replacement for `ntohs` and `ntohl`.
 */
class EndianConvert extends Function {
  EndianConvert() {
    this.getName() = "big" and
    this.getDeclaringType().getName().matches("Endian")
  }
}

/**
 * Holds if `i` is an endianness conversion.
 * (A telltale sign of network data.)
 */
predicate isNetworkData(Instruction i) {
  i.(CallInstruction).getCallTarget().(FunctionInstruction).getFunctionSymbol() instanceof
    EndianConvert
}

/** Holds if `i` is a narrowing conversion. */
predicate isNarrowingConversion(ConvertInstruction i) {
  i.getResultSize() < i.getUnary().getResultSize()
}

class Cfg extends TaintTracking::Configuration {
  Cfg() { this = "FizzOverflowIR" }

  /**
   * Holds if `source` is network data.
   */
  override predicate isSource(DataFlow::Node source) { isNetworkData(source.asInstruction()) }

  /** Holds if `sink` is a narrowing conversion. */
  override predicate isSink(DataFlow::Node sink) { isNarrowingConversion(sink.asInstruction()) }
}

from
  Cfg cfg, DataFlow::PathNode source, DataFlow::PathNode sink, ConvertInstruction conv,
  Type inputType, Type outputType
where
  cfg.hasFlowPath(source, sink) and
  conv = sink.getNode().asInstruction() and
  inputType = conv.getUnary().getResultType() and
  outputType = conv.getResultType()
select sink, source, sink,
  "Conversion of untrusted data from " + inputType + " to " + outputType + "."
